package com.pansoft.xbrl.xbrljson.outputer.impl;

import com.pansoft.xbrl.xbrljson.model.*;
import com.pansoft.xbrl.xbrljson.outputer.XbrlOutputer;
import com.pansoft.xbrl.xbrljson.util.DateUtil;
import com.pansoft.xbrl.xbrljson.util.NumberUtil;
import com.pansoft.xbrl.xbrljson.util.StringUtil;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Namespace;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;

/**
 * 实例文档导出
 * @author coolmayi
 *
 */
public class BaseXbrlOutputer implements XbrlOutputer {

	/**
	 * 获取xbrl实例文档xml数据
	 * @param xbrlData
	 * @return
	 * @throws Exception
	 */
	public String getXbrlXml(Xbrl xbrlData) throws Exception {

		if (xbrlData == null) {
			return null;
		}

		Document doc = this.createXbrlDoc(xbrlData);

		if (doc != null && doc.getRootElement() != null) {
			return doc.getRootElement().asXML();
		}


		return null;
	}



	/**
	 * 实例文档导出
	 */
	public boolean exportXbrl(Xbrl xbrlData, String savePath, String fileName, boolean exportTaxonomy) throws Exception {
		
		if (xbrlData == null) {
			return false;
		}
		
		if (StringUtil.isBlank(savePath)) {
			return false;
		}
		
		if (StringUtil.isBlank(fileName)) {
			fileName = UUID.randomUUID() +".xml";
		}

		Document doc = this.createXbrlDoc(xbrlData);
		
		// 导出文件
		exportFile(doc, savePath, fileName);
		
		return true;
	}

	/**
	 * 创建实例文档那个doc对象
	 * @param xbrlData
	 * @return
	 */
	private Document createXbrlDoc(Xbrl xbrlData) {
		Document doc = createDocument();
		Element xbrlNode = null;
		xbrlNode = doc.addElement("xbrl", "http://www.xbrl.org/2003/instance");

		// 生成命名空间信息
		createNameSpace(xbrlNode, xbrlData.getNameSpaceMap());
		// 添加实例文档必要文件引用
		xbrlNode.addAttribute("xsi:schemaLocation" , "http://www.xbrl.org/2003/linkbase http://www.xbrl.org/2003/xbrl-linkbase-2003-12-31.xsd");
		// 创建schema文件引用
		createSchemaRef(xbrlNode, xbrlData.getSchemaRef());
		// 创建上下文列表
		createContext(xbrlNode, xbrlData.getContextMap(), xbrlData);
		// 创建货币类型
		createUnit(xbrlNode, xbrlData.getUnitList());
		// 创建数据对象
		createInstData(xbrlNode, xbrlData.getInstDataList());

		return doc;
	}


	/**
	 * 导出文件
	 * @param doc
	 * @param path
	 * @param fileName
	 * @return
	 * @throws IOException
	 */
	public boolean exportFile(Document doc, String path, String fileName) throws IOException {
		OutputFormat format = OutputFormat.createPrettyPrint();
		format.setEncoding("UTF-8");
		File f = new File(path);
		if(f.exists() == false){
			f.mkdirs();
		}
		f = new File(path, fileName);
		XMLWriter write = new XMLWriter(new FileOutputStream(f.getAbsoluteFile()), format);
		System.out.println(doc.getRootElement().asXML());
		write.write(doc);
		write.close();
		
		System.out.println("生成文件成功，文件路径：" + f.getAbsolutePath());
		
		return true;
	}
	
	
	/**
	 * 创建实例文档数据
	 * @param parentNode
	 * @param instDataList
	 */
	private void createInstData(Element parentNode, List<InstData> instDataList) {
		
		if (instDataList == null) {
			return ;
		}
		
		for (InstData instData : instDataList) {
			Element dataNode = parentNode.addElement(instData.getElementNsName() +":" + instData.getElementName());
			dataNode.addAttribute("contextRef", instData.getContextRef());
			String strValue = instData.getStrValue();
			Double numValue = instData.getNumValue();
			String val = strValue;
			String nil = instData.getNil();
			boolean isNil = false;
			if (!StringUtil.isBlank(nil) && nil.toLowerCase().equals("true")) {
				isNil = true;
			}
			String id = instData.getId();
			if (!StringUtil.isBlank(id)) {
				dataNode.addAttribute("id", id);
			}
			
			String unitId = instData.getUnitRef();
			if (!StringUtil.isBlank(unitId)) {
				dataNode.addAttribute("unitRef", unitId);
			}
			
			String lang = instData.getLang();
			if (!StringUtil.isBlank(lang)) {
				dataNode.addAttribute("xml:lang", lang);
			}
			
			String dec = instData.getDecimals();
			String precision = instData.getPrecision();
			
			if (!StringUtil.isBlank(dec)) {
				try {
					val = NumberUtil.getNoTailZeroString(numValue, dec);
				} catch (Exception e) {
				}
			}

			if (!isNil && !StringUtil.isBlank(dec)) {
				dataNode.addAttribute("decimals", dec);
			}

			if (!isNil && !StringUtil.isBlank(precision)) {
				dataNode.addAttribute("precision", precision);
			}
			
			if (isNil) {
				dataNode.addAttribute("xsi:nil", "true");
			} else {
//				if (val == null){
//					val = "";
//				}
				if (val != null) {
					dataNode.setText(val);
				}
			}

			List<InstData> childList = instData.getChildList();
			if (childList != null && childList.size() > 0) {
				this.createInstData(dataNode, childList);
			}
		}
	}
	
	
	/**
	 * 创建货币类型
	 * @param parentNode
	 * @param unitMap
	 */
	private void createUnit(Element parentNode, HashMap<String, Unit> unitMap) {
		
		if (unitMap == null) {
			return ;
		}
		
		for (Unit item : unitMap.values()) {
			
			Element uNode = parentNode.addElement("unit");
			uNode.addAttribute("id", item.getId());
			
			// 基础形态
			List<String> measureList = item.getMeasureList();
			if (measureList != null && measureList.size() >0) {
				for (String m : measureList) {
					Element mNode = uNode.addElement("measure");
					mNode.setText(m);
				}
			}
			
			// 分子分母形态
			List<String> numeratorList = item.getNumeratorList();
			List<String> denominatorList = item.getDenominatorList();
			if (numeratorList != null && denominatorList != null
					&& numeratorList.size() > 0 && denominatorList.size() > 0) {
				Element dNode = uNode.addElement("divide");
				if (numeratorList != null) {
					for (String Numerator : numeratorList) {
						Element unNode = dNode.addElement("unitNumerator");
						Element mNode = unNode.addElement("measure");
						mNode.setText(Numerator);
					}
				}
				if (denominatorList != null) {
					for (String Numerator : denominatorList) {
						Element unNode = dNode.addElement("unitDenominator");
						Element mNode = unNode.addElement("measure");
						mNode.setText(Numerator);
					}
				}
			}
		}
		

		
	}
	
	/**
	 * 创建上下文列表
	 * @param parentNode
	 * @param contextMap
	 */
	private void createContext(Element parentNode, HashMap<String, Context> contextMap, Xbrl xbrlData) {
		
		if (contextMap == null || contextMap.size() == 0) {
			return ;
		}
		
		Entity defaultEntity = xbrlData.getDefaultEntity();
		
		for (Context context : contextMap.values()) {
			
			// 填充默认实体信息
			if (defaultEntity != null) {
				if (StringUtil.isBlank(context.getScheme())) {
					context.setScheme(defaultEntity.getScheme());
				}
				if (StringUtil.isBlank(context.getIdentifier())) {
					context.setIdentifier(defaultEntity.getIdentifier());
				}
			}
			createContextNode(parentNode, context);
		}
	}
	
	
	public Element createContextNode(Element parentNode, Context context) {
		if (parentNode == null) {
			return null;
		}
		Context item = context;
		Element cNode = parentNode.addElement("context");
		cNode.addAttribute("id", item.getId());
		
		// 实体信息
		Element entityNode = cNode.addElement("entity");
		Element identifierNode = entityNode.addElement("identifier");
		
		if (item.getScheme() != null) {
			identifierNode.addAttribute("scheme", item.getScheme());
		}
		if (item.getIdentifier() != null) {
			identifierNode.setText(item.getIdentifier());
		}
		
		// 期间数据
		Element periodNode = cNode.addElement("period");
		Element subNode = null;
		int periodType = item.getPeriodType();
		if (periodType == Context.ContextPeriodInstant) {
			subNode = periodNode.addElement("instant");
			subNode.setText(DateUtil.formatDateValue(item.getInstDate()));
		} else if (periodType == Context.ContextPeriodDuration) {
			subNode = periodNode.addElement("startDate");
			subNode.setText(DateUtil.formatDateValue(item.getStartDate()));
			subNode = periodNode.addElement("endDate");
			subNode.setText(DateUtil.formatDateValue(item.getEndDate()));
		} else if(periodType == Context.ContextPeriodForever) {
			subNode = periodNode.addElement("forever");
		}
		
		// scenario 信息
		List<ContextDim> scenarioList = item.getScenarioList();
		if (scenarioList != null && scenarioList.size() > 0) {
			Element scenarioNode = cNode.addElement("scenario");
			for (ContextDim dim : scenarioList) {
				createContextDimNode(dim, scenarioNode);
			}
			
		}
		
		// segment 信息
		List<ContextDim> segmentList = item.getSegmentList();
		if (segmentList != null && segmentList.size() > 0) {
			Element segmentNode = entityNode.addElement("segment");
			for (ContextDim dim : segmentList) {
				createContextDimNode(dim, segmentNode);	
			}
		}
		
		return cNode;
	}
	
	
	private void createContextDimNode(ContextDim dim, Element dimNode) {
		
		Element subNode = null;
		if(dim instanceof TypedMember){
			TypedMember ym = (TypedMember)dim;
			//类型化维度
			subNode = dimNode.addElement("xbrldi:typedMember");
			subNode.addAttribute("dimension", ym.getDimension());
			if (!StringUtil.isBlank(ym.getMember())) {
				Element typeMemNode	=	subNode.addElement(ym.getMember());
				String memberValue = ym.getMemberValue();
				if(!StringUtil.isBlank(memberValue)){
					typeMemNode.setText(memberValue);
				}
			}
		}else if(dim instanceof ExplicitMember){
			ExplicitMember em = (ExplicitMember)dim;
			subNode = dimNode.addElement("xbrldi:explicitMember");
			subNode.addAttribute("dimension", em.getDimension());
			subNode.setText(em.getMember());
		}
	}
	

	
	/**
	 * 创建schema文件引用
	 * @param parentNode
	 * @param schemaRef
	 */
	private void createSchemaRef(Element parentNode, SchemaRef schemaRef) {
		
		if (schemaRef == null) {
			return ;
		}
		Element node = parentNode.addElement("link:schemaRef");
		node.addAttribute("xlink:type", schemaRef.getType());
//		node.addAttribute("xlink:arcrole", schemaRef.get);
		node.addAttribute("xlink:href", schemaRef.getHref());
		node.addAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
		node.addAttribute("xmlns:link", "http://www.xbrl.org/2003/linkbase");		
	}
	
	
	/**
	 * 创建doc对象
	 * @return
	 */
	private Document createDocument() {
		Document doc = DocumentHelper.createDocument();
		
		return doc;
	}
	
	/**
	 * 创建命名空间
	 * @param node
	 * @param nameSpaceMap
	 */
	public void createNameSpace(Element node, HashMap<String, String> nameSpaceMap) {
		
		if (node == null) {
			return ;
		}
		
		if (nameSpaceMap == null || nameSpaceMap.size() == 0) {
			return ;
		}
		
		String uri = null;
		
		for (String prefix : nameSpaceMap.keySet()) {
			uri = nameSpaceMap.get(prefix);
			node.add(new Namespace(prefix, uri));
		}
		
	}
	
	
	
}
